package com.ds.lens.data.service;

import com.alibaba.fastjson.JSONObject;
import com.ds.lens.data.common.document.Event;
import com.ds.lens.data.service.esservice.EventOperateService;
import com.ds.lens.data.vo.in.PagedConditionInVO;
import com.ds.lens.data.vo.in.User;
import com.ds.lens.data.vo.in.v2.event.EventInVO;
import com.ds.lens.data.vo.out.v2.event.EventInfoOutVO;
import com.ds.lens.data.vo.out.v2.event.EventListOutVO;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.lang3.StringUtils;
import org.elasticsearch.index.query.BoolQueryBuilder;
import org.elasticsearch.index.query.QueryBuilders;
import org.elasticsearch.index.query.RangeQueryBuilder;
import org.elasticsearch.index.query.TermQueryBuilder;
import org.elasticsearch.index.query.TermsQueryBuilder;
import org.elasticsearch.index.query.WildcardQueryBuilder;
import org.elasticsearch.search.SearchHit;
import org.elasticsearch.search.aggregations.AggregationBuilders;
import org.elasticsearch.search.aggregations.BucketOrder;
import org.elasticsearch.search.aggregations.bucket.terms.Terms;
import org.elasticsearch.search.aggregations.metrics.tophits.TopHits;
import org.elasticsearch.search.sort.SortBuilders;
import org.elasticsearch.search.sort.SortOrder;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.elasticsearch.core.aggregation.AggregatedPage;
import org.springframework.data.elasticsearch.core.query.NativeSearchQueryBuilder;
import org.springframework.stereotype.Service;

import java.util.ArrayList;
import java.util.List;

/**
 * Description:
 *
 * @author hill
 * @date 2019-11-06
 */
@Slf4j
@Service
public class LensEventService extends BaseService{

    private static final int DEFAULT_SIZE = 200;

    private static final int DEFAULT_SHARD_SIZE = 10000;

    private static final int DEFAULT_TOP_SIZE = 90;

    @Autowired
    EventOperateService eventOperateService;

    public EventListOutVO getEventList(PagedConditionInVO<EventInVO> conditionInVO, User user) {
        if (CollectionUtils.isEmpty(conditionInVO.getCondition().getApplicationIds())) {
            checkUserApplication(user);
            conditionInVO.getCondition().setApplicationIds(user.getApplicationIds());
        } else {
            checkUserApplication(user, conditionInVO.getCondition().getApplicationIds());
        }
        NativeSearchQueryBuilder nativeSearchQueryBuilder = getSearchQueryBuilder(conditionInVO);
        if (conditionInVO.getCondition().isAggr()) {
            nativeSearchQueryBuilder.addAggregation(AggregationBuilders.terms("aggr").field("groupbyKey")
                    .shardSize(DEFAULT_SHARD_SIZE).size(DEFAULT_SIZE).order(BucketOrder.aggregation("timeStamp", false))
                    .subAggregation(AggregationBuilders.max("timeStamp").field("timeStamp"))
                    .subAggregation(AggregationBuilders
                            .topHits("top").sort("timeStamp", SortOrder.DESC)
                            .fetchSource(true).from(0).size(DEFAULT_TOP_SIZE))
            );
        }
        AggregatedPage<Event> events = eventOperateService.searchAggregation(nativeSearchQueryBuilder.build());
        EventListOutVO eventListOutVO = new EventListOutVO();
        List<EventInfoOutVO> list = new ArrayList<>();
        if (CollectionUtils.isEmpty(events.getContent())) {
            return null;
        } else {
            if (conditionInVO.getCondition().isAggr()) {
                Terms terms = events.getAggregations().get("aggr");
                terms.getBuckets().forEach(bucket -> {
                    EventInfoOutVO infoOutVO = new EventInfoOutVO();
                    TopHits top = bucket.getAggregations().get("top");
                    for (SearchHit hit : top.getHits().getHits()) {
                        Event event = JSONObject.parseObject(hit.getSourceRef().toBytesRef().bytes, Event.class);
                        if (infoOutVO.getEvent() == null) {
                            infoOutVO.setEvent(event);
                        }
                        infoOutVO.getStates().add(event.getState());
                    }
                    list.add(infoOutVO);
                });
            } else {
                eventListOutVO.setTotalSize(events.getTotalElements());
                events.getContent().forEach(event -> {
                    EventInfoOutVO infoOutVO = new EventInfoOutVO();
                    infoOutVO.setEvent(event);
                    list.add(infoOutVO);
                });
            }
            eventListOutVO.setList(list);
        }
        return eventListOutVO;
    }

    private NativeSearchQueryBuilder getSearchQueryBuilder(PagedConditionInVO<EventInVO> conditionInVO) {
        NativeSearchQueryBuilder nativeSearchQueryBuilder = new NativeSearchQueryBuilder();
        BoolQueryBuilder queryBuilder = QueryBuilders.boolQuery();

        if (StringUtils.isNotEmpty(conditionInVO.getCondition().getName())) {
            WildcardQueryBuilder wildcardQueryBuilder = QueryBuilders.wildcardQuery("name", conditionInVO.getCondition().getName() + "*");
            queryBuilder.must(wildcardQueryBuilder);
        }
        if (CollectionUtils.isNotEmpty(conditionInVO.getCondition().getTags())) {
            TermsQueryBuilder tagsQueryBuilder = QueryBuilders.termsQuery("tags", conditionInVO.getCondition().getTags());
            queryBuilder.must(tagsQueryBuilder);
        }
        if (CollectionUtils.isNotEmpty(conditionInVO.getCondition().getApplicationIds())) {
            TermsQueryBuilder tagsQueryBuilder = QueryBuilders.termsQuery("appId", conditionInVO.getCondition().getApplicationIds());
            queryBuilder.must(tagsQueryBuilder);
        }
        if (CollectionUtils.isNotEmpty(conditionInVO.getCondition().getStates())) {
            TermsQueryBuilder stateQueryBuilder = QueryBuilders.termsQuery("state", conditionInVO.getCondition().getStates());
            queryBuilder.must(stateQueryBuilder);
        }

        if (StringUtils.isNotEmpty(conditionInVO.getCondition().getGroupValue())) {
            TermsQueryBuilder stateQueryBuilder = QueryBuilders.termsQuery("triggered.groupValue", conditionInVO.getCondition().getGroupValue());
            queryBuilder.must(stateQueryBuilder);
        }

        if (conditionInVO.getCondition().getMonitorId() != null) {
            TermQueryBuilder monitorIdQueryBuilder = QueryBuilders.termQuery("monitorId", conditionInVO.getCondition().getMonitorId());
            queryBuilder.must(monitorIdQueryBuilder);
        }

        if (conditionInVO.getCondition().getVersion() != null) {
            TermQueryBuilder versionQueryBuilder = QueryBuilders.termQuery("version", conditionInVO.getCondition().getVersion());
            queryBuilder.must(versionQueryBuilder);
        }

        if (conditionInVO.getCondition().getGroupbyKey() != null) {
            TermQueryBuilder versionQueryBuilder = QueryBuilders.termQuery("groupbyKey", conditionInVO.getCondition().getGroupbyKey());
            queryBuilder.must(versionQueryBuilder);
        }

        if (conditionInVO.getCondition().getStartTime() != null && conditionInVO.getCondition().getEndTime() != null) {
            RangeQueryBuilder rangeQueryBuilder = QueryBuilders.rangeQuery("timeStamp").gte(conditionInVO.getCondition().getStartTime()).lt(conditionInVO.getCondition().getEndTime());
            queryBuilder.must(rangeQueryBuilder);
        }

        nativeSearchQueryBuilder.withQuery(queryBuilder).withPageable(PageRequest.of(conditionInVO.getPage().getIndex() - 1, conditionInVO.getPage().getSize()));
        String order = SortOrder.DESC.toString();
        if (null != conditionInVO.getSort() && StringUtils.isNotEmpty(conditionInVO.getSort().getOrder())) {
            order = conditionInVO.getSort().getOrder();
        }
        nativeSearchQueryBuilder.withSort(SortBuilders.fieldSort("timeStamp").order(SortOrder.fromString(order)));
        return nativeSearchQueryBuilder;
    }


    public Event getLensEventById(String uid, User user) {
        NativeSearchQueryBuilder nativeSearchQueryBuilder = new NativeSearchQueryBuilder();
        BoolQueryBuilder queryBuilder = QueryBuilders.boolQuery();
        TermQueryBuilder uidQueryBuilder = QueryBuilders.termQuery("uid", uid);
        queryBuilder.must(uidQueryBuilder);
        nativeSearchQueryBuilder.withQuery(queryBuilder);
        Page<Event> events = eventOperateService.searchDetail(nativeSearchQueryBuilder.build());
        if (events.getTotalElements() > 0) {
            Event event = events.getContent().get(0);
            checkUserApplication(user, event.getAppId());
            return event;
        }
        return null;
    }

}
